package com.kexilo.core.common.utils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.function.Function;

/**
 * 查询优化工具类
 * 提供数据库查询性能优化相关的工具方法
 * 
 * @author Kexilo
 */
public class QueryOptimizationUtils {
    
    private static final Logger log = LoggerFactory.getLogger(QueryOptimizationUtils.class);
    
    // 默认分页大小限制
    private static final long DEFAULT_MAX_PAGE_SIZE = 1000;
    private static final long DEFAULT_MAX_OFFSET = 50000;
    
    /**
     * 优化分页查询参数
     * 防止深分页问题，限制最大页码和页大小
     * 
     * @param page 分页对象
     * @return 优化后的分页对象
     */
    public static <T> IPage<T> optimizePagination(IPage<T> page) {
        if (page == null) {
            return new Page<>(1, 10);
        }
        
        long current = page.getCurrent();
        long size = page.getSize();
        
        // 限制页大小
        if (size > DEFAULT_MAX_PAGE_SIZE) {
            log.warn("页大小[{}]超过限制[{}]，已调整", size, DEFAULT_MAX_PAGE_SIZE);
            size = DEFAULT_MAX_PAGE_SIZE;
        }
        
        // 限制偏移量（防止深分页）
        long offset = (current - 1) * size;
        if (offset > DEFAULT_MAX_OFFSET) {
            log.warn("分页偏移量[{}]过大，建议使用游标分页", offset);
            // 调整到最大允许的页码
            current = (DEFAULT_MAX_OFFSET / size) + 1;
        }
        
        return new Page<>(current, size);
    }
    
    /**
     * 创建优化的查询包装器
     * 添加常用的查询优化策略
     * 
     * @param <T> 实体类型
     * @return 优化的查询包装器
     */
    public static <T> QueryWrapper<T> createOptimizedWrapper() {
        QueryWrapper<T> wrapper = new QueryWrapper<>();
        
        // 默认按创建时间倒序（利用索引）
        wrapper.orderByDesc("create_time");
        
        return wrapper;
    }
    
    /**
     * 添加状态过滤条件
     * 利用部分索引优化查询
     * 
     * @param wrapper 查询包装器
     * @param status 状态值
     * @param <T> 实体类型
     * @return 查询包装器
     */
    public static <T> QueryWrapper<T> addStatusFilter(QueryWrapper<T> wrapper, Object status) {
        if (status != null) {
            wrapper.eq("status", status);
        }
        return wrapper;
    }
    
    /**
     * 添加时间范围查询条件
     * 优化时间范围查询性能
     * 
     * @param wrapper 查询包装器
     * @param timeField 时间字段名
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param <T> 实体类型
     * @return 查询包装器
     */
    public static <T> QueryWrapper<T> addTimeRangeFilter(QueryWrapper<T> wrapper, 
                                                         String timeField,
                                                         Object startTime, 
                                                         Object endTime) {
        if (startTime != null) {
            wrapper.ge(timeField, startTime);
        }
        if (endTime != null) {
            wrapper.le(timeField, endTime);
        }
        
        // 如果有时间范围查询，优化排序
        if (startTime != null || endTime != null) {
            wrapper.orderByDesc(timeField);
        }
        
        return wrapper;
    }
    
    /**
     * 添加模糊查询条件（优化版）
     * 避免前置通配符，优化模糊查询性能
     * 
     * @param wrapper 查询包装器
     * @param field 字段名
     * @param value 查询值
     * @param <T> 实体类型
     * @return 查询包装器
     */
    public static <T> QueryWrapper<T> addOptimizedLikeFilter(QueryWrapper<T> wrapper, 
                                                            String field, 
                                                            String value) {
        if (StringUtils.isNotEmpty(value)) {
            // 避免前置通配符（%value），使用后置通配符（value%）
            if (value.startsWith("%")) {
                log.warn("模糊查询[{}]使用前置通配符，可能影响性能", field);
                wrapper.like(field, value);
            } else {
                // 使用后置通配符，可以利用索引
                wrapper.likeRight(field, value);
            }
        }
        return wrapper;
    }
    
    /**
     * 添加IN查询条件（优化版）
     * 限制IN条件的数量，防止SQL过长
     * 
     * @param wrapper 查询包装器
     * @param field 字段名
     * @param values 值列表
     * @param <T> 实体类型
     * @return 查询包装器
     */
    public static <T> QueryWrapper<T> addOptimizedInFilter(QueryWrapper<T> wrapper, 
                                                          String field, 
                                                          List<?> values) {
        if (values != null && !values.isEmpty()) {
            // 限制IN条件数量
            final int MAX_IN_SIZE = 1000;
            if (values.size() > MAX_IN_SIZE) {
                log.warn("IN条件数量[{}]超过建议值[{}]，可能影响性能", values.size(), MAX_IN_SIZE);
                // 截取前1000个
                values = values.subList(0, MAX_IN_SIZE);
            }
            wrapper.in(field, values);
        }
        return wrapper;
    }
    
    /**
     * 游标分页查询
     * 适用于大数据量的分页场景，避免深分页问题
     * 
     * @param lastId 上次查询的最后一个ID
     * @param pageSize 页大小
     * @param queryFunction 查询函数
     * @param <T> 实体类型
     * @return 查询结果
     */
    public static <T> List<T> cursorPagination(Long lastId, 
                                               int pageSize, 
                                               Function<QueryWrapper<T>, List<T>> queryFunction) {
        QueryWrapper<T> wrapper = new QueryWrapper<>();
        
        // 限制页大小
        if (pageSize > DEFAULT_MAX_PAGE_SIZE) {
            pageSize = (int) DEFAULT_MAX_PAGE_SIZE;
        }
        
        // 游标条件
        if (lastId != null && lastId > 0) {
            wrapper.gt("id", lastId);
        }
        
        // 按ID正序排序
        wrapper.orderByAsc("id");
        wrapper.last("LIMIT " + pageSize);
        
        return queryFunction.apply(wrapper);
    }
    
    /**
     * 批量查询优化
     * 将大批量查询拆分为小批次，避免内存溢出
     * 
     * @param totalSize 总数量
     * @param batchSize 批次大小
     * @param batchProcessor 批次处理函数
     * @param <T> 处理结果类型
     * @return 处理结果列表
     */
    public static <T> List<T> batchProcess(int totalSize, 
                                          int batchSize, 
                                          Function<Integer, List<T>> batchProcessor) {
        List<T> results = new java.util.ArrayList<>();
        
        // 限制批次大小
        final int MAX_BATCH_SIZE = 1000;
        if (batchSize > MAX_BATCH_SIZE) {
            batchSize = MAX_BATCH_SIZE;
        }
        
        int batches = (totalSize + batchSize - 1) / batchSize;
        log.debug("批量处理：总数[{}]，批次大小[{}]，批次数[{}]", totalSize, batchSize, batches);
        
        for (int i = 0; i < batches; i++) {
            int offset = i * batchSize;
            List<T> batchResult = batchProcessor.apply(offset);
            if (batchResult != null) {
                results.addAll(batchResult);
            }
        }
        
        return results;
    }
    
    /**
     * 检查查询是否可能导致性能问题
     * 
     * @param wrapper 查询包装器
     * @return 性能检查结果
     */
    public static QueryPerformanceCheck checkQueryPerformance(QueryWrapper<?> wrapper) {
        QueryPerformanceCheck check = new QueryPerformanceCheck();
        
        String sqlSegment = wrapper.getSqlSegment();
        if (StringUtils.isEmpty(sqlSegment)) {
            check.addWarning("查询条件为空，可能导致全表扫描");
            return check;
        }
        
        // 检查是否有前置通配符
        if (sqlSegment.contains("LIKE '%")) {
            check.addWarning("包含前置通配符的LIKE查询，可能无法使用索引");
        }
        
        // 检查是否有ORDER BY但没有WHERE条件
        if (sqlSegment.contains("ORDER BY") && !sqlSegment.contains("WHERE")) {
            check.addWarning("仅有排序无过滤条件，可能导致全表扫描");
        }
        
        // 检查是否有复杂的OR条件
        if (sqlSegment.contains(" OR ")) {
            check.addWarning("包含OR条件，可能影响索引使用效率");
        }
        
        return check;
    }
    
    /**
     * 查询性能检查结果
     */
    public static class QueryPerformanceCheck {
        private final List<String> warnings = new java.util.ArrayList<>();
        private boolean hasIssues = false;
        
        public void addWarning(String warning) {
            warnings.add(warning);
            hasIssues = true;
            log.warn("查询性能警告: {}", warning);
        }
        
        public List<String> getWarnings() {
            return warnings;
        }
        
        public boolean hasIssues() {
            return hasIssues;
        }
    }
}
